package com.tsd.job.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tsd.core.annotation.TaskJob;
import com.tsd.core.annotation.TaskJobMethod;
import com.tsd.core.utils.HlpUtils;
import com.tsd.core.utils.ListUtil;
import com.tsd.core.vo.HlpException;
import com.tsd.core.vo.PageBean;
import com.tsd.core.datasource.SpringContextUtil;
import com.tsd.job.TaskUtils;
import com.tsd.job.dao.SysTaskJobMapper;
import com.tsd.job.entity.SysTaskJob;
import com.tsd.job.entity.SysTaskJobExt;
import com.tsd.job.service.SysTaskJobService;
import com.tsd.service.BaseServiceImpl;
import com.tsd.system.entity.SysUser;
import com.tsd.system.entity.SysUserExt;
import com.tsd.core.thread.ThreadPoolTaskUtil;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 描述：SysTaskJob 服务实现层
 *
 * @author huawei
 * @date 2022/02/20 16:29:26
 */
@Service
public class SysTaskJobServiceImpl extends BaseServiceImpl implements SysTaskJobService {

    @Resource
    private SysTaskJobMapper sysTaskJobMapper;
    @Resource
    private SchedulerFactoryBean schedulerFactoryBean;
    @Resource
    private BeanFactory beanFactory;

    @Override
    public SysTaskJob getSysTaskJobById(Long id) throws Exception {
        return sysTaskJobMapper.selectByPrimaryKey(id);
    }

    @Override
    public SysTaskJobExt queryBySid(String sid) throws Exception {
        return sysTaskJobMapper.selectBySid(sid);
    }

    @Override
    public void deleteSysTaskJobs(List<String> list, SysUser opUser) throws Exception {
        for (String sid : list) {
            SysTaskJobExt old = sysTaskJobMapper.selectBySid(sid);
            super.checkEmpty(old, "该记录不存在");
            super.autoInjectBaseData(old, opUser, TYPE_DELETE);
            sysTaskJobMapper.updateByPrimaryKeySelective(old);
            updateQuartzFactory4Spring(old);
        }
    }

    @Override
    public void saveSysTaskJob(SysTaskJobExt record, SysUser opUser) throws Exception {
        if (HlpUtils.isEmpty(record.getId())) {
            super.autoInjectBaseData(record, opUser, TYPE_CREATE);
            sysTaskJobMapper.insertSelective(record);
        } else {
            super.autoInjectBaseData(record, opUser, TYPE_UPDATE);
            sysTaskJobMapper.updateByPrimaryKeySelective(record);
        }
    }

    /**
     * 更新cron任务
     *
     * @param scheduler
     * @param job
     */
    private void updateCronFactory(Scheduler scheduler, SysTaskJobExt job) throws ClassNotFoundException, SchedulerException {
        TriggerKey triggerKey = TaskUtils.genMyTriggerKey(job);
        JobKey jobKey = TaskUtils.genJobKey(job);
        if (SysTaskJobExt.TASK_STATE_ENABLE.equals(job.getState())) {
            //启用
            Trigger trigger = scheduler.getTrigger(triggerKey);
            // trigger如果为null则说明scheduler中并没有创建该任务
            if (trigger == null) {
                Class<?> jobClass = Class.forName(job.getJob_object_name().trim());
                @SuppressWarnings("unchecked")
                JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) jobClass).withIdentity(jobKey)
                        .withDescription(job.getNote_info()).build();
                trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey)
                        .withSchedule(CronScheduleBuilder.cronSchedule(job.getCron_clause()).withMisfireHandlingInstructionDoNothing())
                        .build();
                scheduler.scheduleJob(jobDetail, trigger);
            } else {
                // 不为null则说明scheduler中有创建该任务,更新即可
                CronTrigger newTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey)
                        .withSchedule(CronScheduleBuilder.cronSchedule(job.getCron_clause()).withMisfireHandlingInstructionDoNothing())
                        .build();
                scheduler.rescheduleJob(triggerKey, newTrigger);
            }
        } else {
            //禁用
            scheduler.pauseJob(jobKey);
        }
    }

    private void updateSimpleFactory(Scheduler scheduler, SysTaskJobExt job) throws SchedulerException, ClassNotFoundException {
        TriggerKey triggerKey = TaskUtils.genMyTriggerKey(job);
        JobKey jobKey = TaskUtils.genJobKey(job);
        if (SysTaskJobExt.TASK_STATE_ENABLE.equals(job.getState())) {
            //启用
            Trigger trigger = scheduler.getTrigger(triggerKey);
            // trigger如果为null则说明scheduler中并没有创建该任务
            if (trigger == null) {
                Class<?> jobClass = Class.forName(job.getJob_object_name().trim());
                @SuppressWarnings("unchecked")
                JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) jobClass).withIdentity(jobKey)
                        .withDescription(job.getNote_info()).build();

                trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey)
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(job.getInterval_time())
                                .withMisfireHandlingInstructionIgnoreMisfires().repeatForever())
                        .build();
                scheduler.scheduleJob(jobDetail, trigger);
            } else {
                // 不为null则说明scheduler中有创建该任务,更新即可
                SimpleTrigger newTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).forJob(jobKey)
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(job.getInterval_time())
                                .withMisfireHandlingInstructionIgnoreMisfires().repeatForever())
                        .build();
                scheduler.rescheduleJob(triggerKey, newTrigger);
            }
        } else {
            //禁用
            scheduler.pauseJob(jobKey);
        }
    }

    @Override
    public void updateQuartzFactory(SysTaskJobExt job) throws SchedulerException, ClassNotFoundException, HlpException {
        job = sysTaskJobMapper.selectBySid(job.getSid());
        super.checkEmpty(job, "记录不存在");
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        if (SysTaskJobExt.TASK_TYPE_CRON.equals(job.getTask_type())) {
            updateCronFactory(scheduler, job);
        } else {
            updateSimpleFactory(scheduler, job);
        }
    }

    @Override
    public void updateQuartzFactory4Spring(SysTaskJobExt jobExt) throws Exception {
        jobExt = sysTaskJobMapper.selectBySid(jobExt.getSid());
        super.checkEmpty(jobExt, "任务不存在");
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        if (SysTaskJobExt.TASK_STATE_ENABLE.equals(jobExt.getState()) && jobExt.getDr() == 0) {
            MethodInvokingJobDetailFactoryBean jobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean();
            jobDetailFactoryBean.setBeanFactory(beanFactory);
            jobDetailFactoryBean.setBeanName(jobExt.getJobBeanName());
            jobDetailFactoryBean.setConcurrent(true);
            jobDetailFactoryBean.setTargetBeanName(jobExt.getJob_object_name());
            jobDetailFactoryBean.setTargetMethod(jobExt.getJob_method_name());
            jobDetailFactoryBean.setArguments(jobExt);
            jobDetailFactoryBean.afterPropertiesSet();
            JobDetail jobDetail = Objects.requireNonNull(jobDetailFactoryBean.getObject());
            if (SysTaskJobExt.TASK_TYPE_CRON.equals(jobExt.getTask_type())) {
                this.updateCronFactory4Spring(scheduler, jobDetail, jobExt);
            } else {
                this.updateSimpleFactory4Spring(scheduler, jobDetail, jobExt);
            }
        } else {
            scheduler.deleteJob(jobExt.getJobKey());
        }
    }

    private void updateCronFactory4Spring(Scheduler scheduler, JobDetail jobDetail, SysTaskJobExt jobExt) throws Exception {
        // 判断触发器是否存在
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(jobExt.getTriggerKey());
        if (trigger == null) {
            CronTriggerFactoryBean triggerFactoryBean = new CronTriggerFactoryBean();
            triggerFactoryBean.setBeanName(jobExt.getTriggerBeanName());
            triggerFactoryBean.setJobDetail(jobDetail);
            triggerFactoryBean.setCronExpression(jobExt.getCron_clause());
            triggerFactoryBean.afterPropertiesSet();
            try {
                JobDetail detail = scheduler.getJobDetail(jobExt.getJobKey());
                if (detail != null) {
                    scheduler.deleteJob(jobExt.getJobKey());
                }
                scheduler.scheduleJob(jobDetail, triggerFactoryBean.getObject());
            } catch (SchedulerException e) {
                if (e.getMessage().contains("will never fire")) {
                    throw new HlpException("cron表达式配置了不合法或过去的时间");
                }
                throw e;
            }
        } else {
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobExt.getCron_clause());
            trigger = trigger.getTriggerBuilder().withIdentity(jobExt.getTriggerKey())
                    .forJob(jobDetail).withSchedule(scheduleBuilder).build();
            scheduler.rescheduleJob(jobExt.getTriggerKey(), trigger);
        }
    }

    private void updateSimpleFactory4Spring(Scheduler scheduler, JobDetail jobDetail, SysTaskJobExt jobExt) throws Exception {
        // 判断触发器是否存在
        SimpleTrigger trigger = (SimpleTrigger) scheduler.getTrigger(jobExt.getTriggerKey());
        if (trigger != null) {
            scheduler.deleteJob(jobExt.getJobKey());
        }
        //由于直接采用rescheduleJob，会出现异常org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'DEFAULT.sys_
        //所以改为直接删除job，然后再创建
        SimpleTriggerFactoryBean triggerFactoryBean = new SimpleTriggerFactoryBean();
        triggerFactoryBean.setBeanName(jobExt.getTriggerBeanName());
        triggerFactoryBean.setJobDetail(jobDetail);
        triggerFactoryBean.setRepeatInterval(jobExt.getInterval_time());
        triggerFactoryBean.afterPropertiesSet();
        scheduler.scheduleJob(jobDetail, triggerFactoryBean.getObject());
//        if (trigger == null) {
//        } else {
        //如下是更新的代码，可保留在此
//            SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
//            scheduleBuilder.withIntervalInMilliseconds(jobExt.getInterval_time());
//            trigger = trigger.getTriggerBuilder().withIdentity(jobExt.getTriggerKey())
//                    .forJob(jobDetail).withSchedule(scheduleBuilder).build();
//            scheduler.rescheduleJob(jobExt.getTriggerKey(), trigger);
//        }
    }

    @Override
    public List<SysTaskJobExt> findAllEnable() throws Exception {
        return sysTaskJobMapper.selectAllEnable();
    }

    @Override
    public List<SysTaskJobExt> findSysTaskJobs(Map<String, Object> params, PageBean page) throws Exception {
        return sysTaskJobMapper.selectByPage(params, page);
    }

    @Override
    public List<SysTaskJobExt> findSysTaskJobs(Map<String, Object> params) throws Exception {
        return sysTaskJobMapper.selectByParams(params);
    }

    @Override
    public List<SysTaskJobExt> queryByEntity(SysTaskJobExt params) {
        return sysTaskJobMapper.selectByEntity(params);
    }

    private static final List<Object> OBJECT_METHOD_LIST = new ArrayList<>();

    @Override
    public List<Object> getJobObjectAndMethodList() {
        if (!HlpUtils.isEmptyList(OBJECT_METHOD_LIST)) {
            return OBJECT_METHOD_LIST;
        }
        List<Object> list = new ArrayList<>();
        ApplicationContext context = SpringContextUtil.getApplicationContext();
        Map<String, Object> map = context.getBeansWithAnnotation(TaskJob.class);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            TaskJob taskJob = AnnotationUtils.findAnnotation(clazz, TaskJob.class);
            if (taskJob == null) {
                continue;
            }
            JSONObject object = new JSONObject();
            object.put("object_name", entry.getKey());
            object.put("object_desc", taskJob.name());
            object.put("mode", taskJob.mode());
            JSONArray methodArray = new JSONArray();
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(TaskJobMethod.class)) {
                    TaskJobMethod jobMethod = method.getAnnotation(TaskJobMethod.class);
                    if (jobMethod == null) {
                        continue;
                    }
                    JSONObject methodObj = new JSONObject();
                    methodObj.put("method_name", method.getName());
                    methodObj.put("method_desc", jobMethod.name());
                    methodArray.add(methodObj);
                }
            }
            object.put("methods", methodArray);
            list.add(object);
            OBJECT_METHOD_LIST.add(object);
        }
        return list;
    }

    @Override
    public void startQuartz(List<String> list, SysUserExt opUser) throws Exception {
        SysTaskJobExt params = new SysTaskJobExt();
        params.setDr(0);
        params.setSid_list(list);
        List<SysTaskJobExt> jobExtList = sysTaskJobMapper.selectByEntity(params);
        if (HlpUtils.isEmptyList(jobExtList)) {
            return;
        }
        List<SysTaskJobExt> filterList = ListUtil.filter(jobExtList, t -> SysTaskJobExt.TASK_STATE_ENABLE.equals(t.getState()));
        if (!HlpUtils.isEmptyList(filterList)) {
            List<String> nameList = ListUtil.map(filterList, SysTaskJobExt::getName);
            throw new HlpException("任务：" + StringUtils.join(nameList, "、") + " 已启动");
        }
        filterList = ListUtil.filter(jobExtList, t -> HlpUtils.isEmpty(t.getTask_type()));
        if (!HlpUtils.isEmptyList(filterList)) {
            List<String> nameList = ListUtil.map(filterList, SysTaskJobExt::getName);
            throw new HlpException("任务：" + StringUtils.join(nameList, "、") + " 任务类型不能为空");
        }
        filterList = ListUtil.filter(jobExtList, t -> SysTaskJobExt.TASK_TYPE_SIMPLE.equals(t.getTask_type()) && HlpUtils.isEmpty(t.getInterval_time()));
        if (!HlpUtils.isEmptyList(filterList)) {
            List<String> nameList = ListUtil.map(filterList, SysTaskJobExt::getName);
            throw new HlpException("任务：" + StringUtils.join(nameList, "、") + " 间隔时间不能为空");
        }
        filterList = ListUtil.filter(jobExtList, t -> SysTaskJobExt.TASK_TYPE_CRON.equals(t.getTask_type()) && HlpUtils.isEmpty(t.getCron_clause()));
        if (!HlpUtils.isEmptyList(filterList)) {
            List<String> nameList = ListUtil.map(filterList, SysTaskJobExt::getName);
            throw new HlpException("任务：" + StringUtils.join(nameList, "、") + " cron表达式不能为空");
        }
        for (SysTaskJobExt jobExt : jobExtList) {
            jobExt.setState(SysTaskJobExt.TASK_STATE_ENABLE);
            super.autoInjectBaseData(jobExt, opUser, this.TYPE_UPDATE);
        }
        ThreadPoolTaskUtil.executeInList4Split(jobExtList, false, itemList -> sysTaskJobMapper.updateList(jobExtList));

        for (SysTaskJobExt jobExt : jobExtList) {
            this.updateQuartzFactory4Spring(jobExt);
        }
    }

    @Override
    public void stopQuartz(List<String> list, SysUserExt opUser) throws Exception {
        SysTaskJobExt params = new SysTaskJobExt();
        params.setDr(0);
        params.setSid_list(list);
        List<SysTaskJobExt> jobExtList = sysTaskJobMapper.selectByEntity(params);
        if (HlpUtils.isEmptyList(jobExtList)) {
            return;
        }
        List<SysTaskJobExt> filterList = ListUtil.filter(jobExtList, t -> SysTaskJobExt.TASK_STATE_DISABLE.equals(t.getState()));
        if (!HlpUtils.isEmptyList(filterList)) {
            List<String> nameList = ListUtil.map(filterList, SysTaskJobExt::getName);
            throw new HlpException("任务：" + StringUtils.join(nameList, "、") + " 已停止");
        }
        for (SysTaskJobExt jobExt : jobExtList) {
            jobExt.setState(SysTaskJobExt.TASK_STATE_DISABLE);
            super.autoInjectBaseData(jobExt, opUser, this.TYPE_UPDATE);
        }
        ThreadPoolTaskUtil.executeInList4Split(jobExtList, false, itemList -> sysTaskJobMapper.updateList(jobExtList));

        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        if (scheduler.isShutdown()) {
            return;
        }
        for (SysTaskJobExt jobExt : jobExtList) {
            if (SysTaskJobExt.TASK_TYPE_CRON.equals(jobExt.getTask_type())) {
                CronTrigger trigger = (CronTrigger) scheduler.getTrigger(jobExt.getTriggerKey());
                if (trigger == null) {
                    continue;
                }
                JobDetail detail = scheduler.getJobDetail(jobExt.getJobKey());
                if (detail != null) {
                    scheduler.deleteJob(jobExt.getJobKey());
                }
            } else {
                SimpleTrigger trigger = (SimpleTrigger) scheduler.getTrigger(jobExt.getTriggerKey());
                if (trigger == null) {
                    continue;
                }
                scheduler.deleteJob(jobExt.getJobKey());
            }
        }
    }
}
